/*
 
 File: audio_helper.cpp
 
 Abstract: Helper functions for acquiring audio data
 
 Portions of this file taken from Apple demo code.
 
 Author: Jasper Speicher, Tellart LLC 2008
 
 */



#include <AudioToolbox/AudioToolbox.h>
#include <AudioUnit/AudioUnit.h>
#include <stdio.h>

#include "CAXException.h"
#include "CAStreamBasicDescription.h"
#include "audio_helper.h"


// Synthesis parameters
//UInt16 weird_sine_counter = 0;
float weird_sine_amp = 1.0f;
float weird_sine_freq = 440.0f;
float weird_samp_freq = 44100.0f;
float weird_sine_angle;
float weird_sine_sampleF;
//float weird_sine_last_sampleF = 1.0f;
float weird_lut[44100];

//UInt16 sine_counter = 0;
float sine_amp = 1.0f;
float sine_freq = 440.0f;
float samp_freq = 44100.0f;
float sine_angle;
float sine_sampleF;
//float sine_last_sampleF = 1.0f;
float sine_lut[44100];

void rioInterruptionListener(	void	*inUserData,
							 UInt32	inInterruption)
{
	printf("Session interrupted! --- %s ---", inInterruption == kAudioSessionBeginInterruption ? "Begin Interruption" : "End Interruption");
	
	AudioUnit *remoteIO = (AudioUnit*)inUserData;
	
	if (inInterruption == kAudioSessionEndInterruption)
	{
		// make sure we are again the active session
		AudioSessionSetActive(true);
		AudioOutputUnitStart(*remoteIO);
	}
}

int SetupRemoteIO (AudioUnit& inRemoteIOUnit, AURenderCallbackStruct inRenderProc, CAStreamBasicDescription& outFormat)
{	
    //setup for effects
    effect_WeirdPitchShifter_setup();
    effect_SineGenerator_setup();
    
	try {		
		// Open the output unit
		AudioComponentDescription desc;
		desc.componentType = kAudioUnitType_Output;
		desc.componentSubType = kAudioUnitSubType_RemoteIO;
		desc.componentManufacturer = kAudioUnitManufacturer_Apple;
		desc.componentFlags = 0;
		desc.componentFlagsMask = 0;
		
		AudioComponent comp = AudioComponentFindNext(NULL, &desc);
		
		XThrowIfError(AudioComponentInstanceNew(comp, &inRemoteIOUnit), "couldn't open the remote I/O unit");
        
		UInt32 one = 1;
		XThrowIfError(AudioUnitSetProperty(inRemoteIOUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, 1, &one, sizeof(one)), "couldn't enable input on the remote I/O unit");
        
		XThrowIfError(AudioUnitSetProperty(inRemoteIOUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, 0, &inRenderProc, sizeof(inRenderProc)), "couldn't set remote i/o render callback");
		
        #if __IPHONE_2_0
        //for OS 2.0

        #else
        //for OS 3.x
        outFormat.SetAUCanonical(2, false);
        #endif
        
		XThrowIfError(AudioUnitSetProperty(inRemoteIOUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &outFormat, sizeof(outFormat)), "couldn't set the remote I/O unit's output client format");
		XThrowIfError(AudioUnitSetProperty(inRemoteIOUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 1, &outFormat, sizeof(outFormat)), "couldn't set the remote I/O unit's input client format");
		
		XThrowIfError(AudioUnitInitialize(inRemoteIOUnit), "couldn't initialize the remote I/O unit");
		
	}
	catch (CAXException &e) {
		char buf[256];
		fprintf(stderr, "audio_helper Error: %s (%s)\n", e.mOperation, e.FormatError(buf));
		return 1;
	}
	catch (...) {
		fprintf(stderr, "An unknown error occurred\n");
		return 1;
	}	
	
	return 0;
}
/*
 * GetPeakAmp
 * Returns the maximum distance the waveform in the buffer travels from 0, in either the + or - direction.
 * Note that the peak is compared only to the other sample values in the buffer passed,
 * and it is reset each time the funciton is called (it is not a running computation).
 */
AudioSampleType GetPeakAmp(AudioBufferList *ioData)
{
	UInt32 bindex = 0;
	int i;
    
    AudioSampleType *data_ptr = (AudioSampleType *)(ioData->mBuffers[bindex].mData);
    AudioSampleType peak = 0;
    AudioSampleType sample_abs = 0;
    for (i = 1; i < ioData->mBuffers[0].mDataByteSize/2; i+=2) {
        sample_abs = fabs(data_ptr[i]);
        if (sample_abs >peak) peak = sample_abs;
    }
    return peak;
}

/************************************************************************/
/* 
 * WeirdPitchShifterPopulateSinfLut
 * Fills a buffer with a sinusoid function so that it can be looked up later, rather than calculated in real time.
 */
void effect_WeirdPitchShifter_setup(){
    float angle = 0;
    int i;
    for (i = 0; i < 44100; i++) {
        angle += ((1.0f/44100.0f) * 6.2831853f);
        angle = fmodf(angle,6.2831853f);
        weird_lut[i] = sinf(angle);
    }
}
/*
 * effect_WeirdPitchShifter_SinfLut
 * Returns a precalculated value of a sinusoid stored in a lookup table.
 */
float effect_WeirdPitchShifter_SinfLut(float t){
    return weird_lut[(int)(t*44099.0f/6.2831853f)];
}
/*
 * effect_WeirdPitchShifter
 * Modulates the incoming data over a sine wave
 */
void effect_WeirdPitchShifter(AudioBufferList *ioData)
{
	UInt32 bindex = 0;
	int i;
    AudioSampleType *data_ptr = (AudioSampleType *)(ioData->mBuffers[bindex].mData);
    for (i = 1; i < ioData->mBuffers[0].mDataByteSize/2; i+=2) {
        weird_sine_angle += ((weird_sine_freq/weird_samp_freq) * 6.2831853f);
        weird_sine_angle = fmodf(weird_sine_angle,6.2831853f);
        weird_sine_sampleF = effect_WeirdPitchShifter_SinfLut(weird_sine_angle) * weird_sine_amp;
        data_ptr[i] = static_cast<AudioSampleType>(weird_sine_sampleF * 255.0f);
    }
}


/************************************************************************/
/*
 * void effect_SineGenerator_setup
 * Set up the sine generator
 */
void effect_SineGenerator_setup()
{
    float angle = 0;
    int i;
    for (i = 0; i < 44100; i++) {
        angle += ((1.0f/44100.0f) * 6.2831853f);
        angle = fmodf(angle,6.2831853f);
        sine_lut[i] = sinf(angle);
    }
}

/*
 * effect_WeirdPitchShifter_SinfLut
 * Returns a precalculated value of a sinusoid stored in a lookup table.
 */
float effect_SineGenerator_SinfLut(float t){
    return sine_lut[(int)(t*44099.0f/6.2831853f)];
}


/*
 * effect_SineGenerator
 * Generates a pure sine wave
 */
void effect_SineGenerator(AudioBufferList *ioData)
{
	UInt32 bindex = 0;
	int i;
    AudioSampleType *data_ptr = (AudioSampleType *)(ioData->mBuffers[bindex].mData);
    UInt32 stepSize = 1;
    for (i = 1; i < ioData->mBuffers[0].mDataByteSize; i+=stepSize) {
        sine_angle += ((sine_freq/samp_freq) * 6.2831853f);
        sine_angle = fmodf(sine_angle,6.2831853f);
        sine_sampleF = effect_SineGenerator_SinfLut(sine_angle) * sine_amp;
        data_ptr[i] = static_cast<AudioSampleType>(sine_sampleF * 255.0f);
    }
}

/************************************************************************/
/*
 * generates silence
 */
void effect_Silence(AudioBufferList *inData)
{
	for (UInt32 i=0; i < inData->mNumberBuffers; i++)
		memset(inData->mBuffers[i].mData, 0, inData->mBuffers[i].mDataByteSize);
}




